package com.itheima.prize.msg;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.itheima.prize.commons.config.RedisKeys;
import com.itheima.prize.commons.db.entity.*;
import com.itheima.prize.commons.db.service.CardGameProductService;
import com.itheima.prize.commons.db.service.CardGameRulesService;
import com.itheima.prize.commons.db.service.CardGameService;
import com.itheima.prize.commons.db.service.GameLoadService;
import com.itheima.prize.commons.utils.RedisUtil;
import org.apache.commons.lang3.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * 活动信息预热，每隔1分钟执行一次
 * 查找未来1分钟内（含），要开始的活动
 */
@Component
public class GameTask {
    private final static Logger log = LoggerFactory.getLogger(GameTask.class);
    @Autowired
    private CardGameService gameService;
    @Autowired
    private CardGameProductService gameProductService;
    @Autowired
    private CardGameRulesService gameRulesService;
    @Autowired
    private GameLoadService gameLoadService;
    @Autowired
    private RedisUtil redisUtil;

    @Scheduled(cron = "*/30 * * * * ?")
    public void execute() {
        System.out.printf("scheduled!"+new Date());
        LocalDateTime now = LocalDateTime.now();

        List<CardGame> games = gameService.list(new QueryWrapper<CardGame>()
                .gt("starttime", now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")))
                .le("starttime", now.plusMinutes(1).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))));
        for (CardGame game : games) {
            gameTask(game);
        }
    }

    public void gameTask(CardGame cardGame){
        long expire = (cardGame.getEndtime().getTime() - System.currentTimeMillis()) / 1000;

        //缓存活动基本信息
        redisUtil.set(RedisKeys.INFO+cardGame.getId(),cardGame,-1);

        //缓存活动策略信息
        List<CardGameRules> rules = gameRulesService.list(new QueryWrapper<CardGameRules>()
                .eq("gameid", cardGame.getId()));
        for (CardGameRules cardGameRule : rules) {
            redisUtil.hset(RedisKeys.MAXGOAL + cardGame.getId(), String.valueOf(cardGameRule.getUserlevel()), cardGameRule.getGoalTimes(), expire);
            redisUtil.hset(RedisKeys.MAXENTER + cardGame.getId(), String.valueOf(cardGameRule.getUserlevel()), cardGameRule.getEnterTimes(), expire);
            redisUtil.hset(RedisKeys.RANDOMRATE + cardGame.getId(), String.valueOf(cardGameRule.getUserlevel()), cardGameRule.getRandomRate(), expire);
        }

        //将奖品生成令牌桶和奖品的映射关系
        List<CardProductDto> cardProducts = gameLoadService.getByGameId(cardGame.getId());
        if (cardProducts != null && cardProducts.size() > 0) {
            return;
        }

        ArrayList<Long> tokenList = new ArrayList<>();
        for (CardProductDto cardProduct : cardProducts) {
            for (int i = 0; i < cardProduct.getAmount(); i++) {
                Long token = getToken(cardGame.getStarttime().getTime(), cardGame.getEndtime().getTime());
                tokenList.add(token);
                redisUtil.set(RedisKeys.TOKEN + cardGame.getId() + "_" + token, cardProduct, expire);
            }
        }
        // 令牌即奖品发放的时间点。从小到大排序后从右侧入队。
        Collections.sort(tokenList);
        redisUtil.rightPushAll(RedisKeys.TOKENS + cardGame.getId(),tokenList);
        redisUtil.expire(RedisKeys.TOKEN + cardGame.getId(),expire);

        // 更新活动状态
        cardGame.setStatus(1);
        gameService.updateById(cardGame);
    }

    //
    private Long getToken(Long start, Long end) {
        //活动持续时间（ms）
        long duration = end - start;
        long rnd = start + new Random().nextInt((int)duration);
        //为什么乘1000，再额外加一个随机数呢？ - 防止时间段奖品多时重复
        return rnd * 1000 + new Random().nextInt(999);
    }
}
